#ifndef __OJ_MODEL2_HPP__
#define __OJ_MODEL2_HPP__

#include <iostream>
#include <string>
#include <unordered_map>
#include <fstream>
#include <mutex>
#include <cassert>
#include <cstring>
#include <mysql/mysql.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"

namespace ns_model
{
    using namespace ns_log;
    using namespace ns_util;

    /*题目类 主要是对于题目信息进行统一管理*/
    class Question
    {
    public:
        std::string _number; // 题目ID，唯一
        std::string _title;  // 题目标题
        std::string _star;   // 题目难度  简单 中等 困难
        int _cpu_limit;      // 题目要求的时间要求
        int _mem_limit;      // 题目要求的空间要求
        std::string _desc;   // 题目具体内容的描述
        std::string _header; // 题目预设给用户在线编译器的代码
        std::string _tail;   // 题目的测试用例，需要和_header拼接，形成完成代码
    };

    /*Model类 主要是对于数据进行管理，提供加载、获取、查找题目等功能*/
    const std::string host = "127.0.0.1";
    const std::string user = "oj_client";
    const std::string passwd = "12345678";
    const std::string db = "oj_online";
    const int port = 3306;
    const std::string oj_questions = "questions";
    class Model
    {
    private:
        MYSQL *_mysql = nullptr;
        std::mutex _mtx;

    public:
        Model()
        {
            _mysql = mysql_init(nullptr);
            if (_mysql == nullptr)
            {
                LOG(FATAL) << " 数据库初始化失败!!!," << mysql_error(_mysql) << "\n";
                exit(3);
            }

            if (mysql_real_connect(_mysql, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
            {
                LOG(FATAL) << " 数据库连接失败!!!," << mysql_error(_mysql) << "\n";
                exit(4);
            }

            mysql_set_character_set(_mysql, "utf8");
            LOG(INFO) << " 数据库连接成功!!!"
                      << "\n";
        }
        ~Model()
        {
            if (_mysql != nullptr)
                mysql_close(_mysql);
        }

        bool MysqlQuery(const std::string &query, std::vector<Question> *out)
        {
            /*执行SQL语句*/
            if (0 != mysql_query(_mysql, query.c_str()))
            {
                LOG(ERROR) << " 数据库执行sql语句失败: " << query << " 原因: " << mysql_error(_mysql) << "\n";
                return false;
            }

            /*提取SQL执行结果*/
            MYSQL_RES *res=nullptr;
            /*加锁保护*/
            {
                std::unique_lock<std::mutex> lock(_mtx);
                res = mysql_store_result(_mysql);
                if(res==nullptr)
                {
                    LOG(ERROR)<<" 获取SQL语句执行结果失败,原因: "<<mysql_error(_mysql)<<"\n";
                    return false;
                }
            }

            /*获取行列数*/
            int rows = mysql_num_rows(res);
            int cols = mysql_num_fields(res);

            /*获取SQL执行结果*/
            Question q;
            for (int i = 0; i < rows; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                q._number = row[0];
                q._title = row[1];
                q._star = row[2];
                q._desc = row[3];
                q._header = row[4];
                q._tail = row[5];
                q._cpu_limit = atoi(row[6]);
                q._mem_limit = atoi(row[7]);
                out->push_back(q);
            }

            mysql_free_result(res);
            return true;
        }

        /*获取列表所有题目信息*/
        bool GetAllQuestions(std::vector<Question> *out)
        {
            std::string sql = "select * from ";
            sql += oj_questions;
            return MysqlQuery(sql, out);
        }

        /*根据题目编号指定获取题目信息*/
        bool GetOneQuestion(const std::string &number, Question *q)
        {
            std::string sql = "select * from ";
            sql += oj_questions;
            sql += " where number=";
            sql += number;
            std::vector<Question> res;
            bool ret = false;
            if (MysqlQuery(sql, &res) == true)
            {
                if (res.size() == 1)
                {
                    *q = res[0];
                    ret = true;
                }
            }
            return ret;
        }
    };
}

#endif